from django.utils.translation import gettext_lazy as _


class MethodNotFoundError(LookupError):
    """Registry method was not found

    Check that the appropriate method has been registered
    """
    def __init__(self, code, methods):
        super().__init__(f"{code} not found in {[m.code for m in methods]}")


class MethodBase:
    code = None
    verbose_name = None
    form_path = None

    def get_devices(self, user):
        raise NotImplementedError()

    def get_other_authentication_devices(self, user, main_device):
        devices = self.get_devices(user)
        return (
            device for device in devices
            if (type(device) is not type(main_device)) or (device.pk != main_device.pk)
        )

    def recognize_device(self, device):
        """
        Return True if the device can be handled by this method.
        """
        return False

    def get_setup_forms(self, wizard):
        """
        Return a dict where keys are setup wizard step names, and the values
        the form class matching the step.
        """
        return {}  # pragma: no cover

    def get_device_from_setup_data(self, request, setup_data):
        """
        Obtain device instance from 2fa setup wizard data.
        """
        return None  # pragma: no cover

    def get_token_form_class(self):
        """
        Return the authentication token form class.
        """
        from two_factor.forms import AuthenticationTokenForm

        return AuthenticationTokenForm

    def get_action(self, device):
        raise NotImplementedError()

    def get_verbose_action(self, device):
        raise NotImplementedError()


class GeneratorMethod(MethodBase):
    code = 'generator'
    verbose_name = _('Token generator')
    form_path = 'two_factor.forms.TOTPDeviceForm'

    def get_devices(self, user):
        return user.totpdevice_set.all()

    def get_setup_forms(self, *args):
        from two_factor.forms import TOTPDeviceForm

        return {'generator': TOTPDeviceForm}

    def get_action(self, device):
        return _('Enter the token generated by your token generator')

    def get_verbose_action(self, device):
        return _('Please enter the token generated by your token generator.')


class MethodRegistry:
    _methods = []

    def __init__(self):
        self.register(GeneratorMethod())

    def register(self, method):
        for registered_method in self._methods:
            if method.code == registered_method.code:
                return   # Already registered, ignore.

        self._methods.append(method)

    def unregister(self, code):
        self._methods = [m for m in self._methods if m.code != code]

    def get_method(self, code):
        try:
            return [meth for meth in self._methods if meth.code == code][0]
        except IndexError:
            raise MethodNotFoundError(code, self._methods)

    def get_methods(self):
        return self._methods

    def method_from_device(self, device):
        for method in self._methods:
            if method.recognize_device(device):
                return method
        # Default to GeneratorMethod
        return GeneratorMethod()


registry = MethodRegistry()
